package org.liaohailong.victorlibrary.widget

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.*
import android.text.TextPaint
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import android.view.View

/**
 * Author: liaohailong
 * Time: 2020/8/6 09:19
 * Describe: 测试Matrix使用
 */
class MatrixTestView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) :
        View(context, attrs, defStyleAttr) {
    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
    constructor(context: Context) : this(context, null)

    private val drawMatrix: Matrix = Matrix()
    private val drawRect: RectF = RectF()
    private val drawPaint: Paint = Paint().apply {
        isAntiAlias = true
        style = Paint.Style.FILL_AND_STROKE
        color = Color.parseColor("#80FF56FF")
    }
    private val drawPath: Path = Path()
    private val debugPaint: Paint = Paint().apply {
        isAntiAlias = true
        style = Paint.Style.FILL_AND_STROKE
        color = Color.parseColor("#1AC2EE11")
    }
    private val textPaint: Paint = TextPaint().apply {
        isAntiAlias = true
        style = Paint.Style.FILL_AND_STROKE
        color = Color.BLACK
        textSize = 15 * 3f
    }


    private val limitRectF = RectF()
    private val touchMatrix = Matrix()

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        limitRectF.setEmpty()
        // 画个正方形，居中显示
        if (w > h) {
            val length = h.toFloat()
            val left = w / 2f - length / 2f
            val top = 0f
            drawRect.set(left, top, left + length, top + length)
        } else {
            val length = w.toFloat()
            val left = 0F
            val top = h / 2f - length / 2f
            drawRect.set(left, top, left + length, top + length)
        }
        limitRectF.set(drawRect)
        drawMatrix.mapRect(drawRect)
    }

    private var state = State.NONE_POINT
    private val lastPoint0 = PointF()
    private val lastPoint1 = PointF()
    private val touchRegion = Region()
    private val drawRegion = Region()

    @SuppressLint("ClickableViewAccessibility")
    override fun onTouchEvent(event: MotionEvent?): Boolean {
        event?.apply {
            when (action) {
                MotionEvent.ACTION_DOWN -> {
                    parent.requestDisallowInterceptTouchEvent(true)
                    lastPoint0.set(x, y)
                    state = State.SINGLE_POINT

                    // 判断是否落在圆内
                    drawRegion.setEmpty()
                    drawRegion.set(
                            drawRect.left.toInt(),
                            drawRect.top.toInt(),
                            drawRect.right.toInt(),
                            drawRect.bottom.toInt())
                    touchRegion.setEmpty()
                    return if (touchRegion.setPath(drawPath, drawRegion)) {
                        touchRegion.contains(x.toInt(), y.toInt())
                    } else false
                }
                MotionEvent.ACTION_MOVE -> {
                    if (pointerCount >= 2) {
                        val x0 = getX(0)
                        val y0 = getY(0)
                        val x1 = getX(1)
                        val y1 = getY(1)

                        if (state == State.SINGLE_POINT) {
                            state = State.MULTI_POINT
                            // 单指变多指，记录第二指位置，等待下次move，计算具体偏移量
                            lastPoint0.set(x0, y0)
                            lastPoint1.set(x1, y1)
                        } else {
                            val rightX = x0.coerceAtLeast(x1)
                            val leftX = x0.coerceAtMost(x1)
                            val topY = y0.coerceAtMost(y1)
                            val bottomY = y0.coerceAtLeast(y1)

                            val lastRightX = lastPoint0.x.coerceAtLeast(lastPoint1.x)
                            val lastLeftX = lastPoint0.x.coerceAtMost(lastPoint1.x)
                            val lastTopY = lastPoint0.y.coerceAtMost(lastPoint1.y)
                            val lastBottomY = lastPoint0.y.coerceAtLeast(lastPoint1.y)

                            val thisXDistance = rightX - leftX
                            val thisYDistance = bottomY - topY
                            val lastXDistance = lastRightX - lastLeftX
                            val lastYDistance = lastBottomY - lastTopY

                            val xScale = thisXDistance / lastXDistance
                            val yScale = thisYDistance / lastYDistance
                            val scale = xScale.coerceAtLeast(yScale)

                            val centerX = leftX + (thisXDistance / 2f)
                            val centerY = topY + (thisYDistance / 2f)
                            val lastCenterX = lastLeftX + (lastXDistance / 2f)
                            val lastCenterY = lastTopY + (lastYDistance / 2f)

                            val xTrans = centerX - lastCenterX
                            val yTrans = centerY - lastCenterY

                            Log.i("Victor", "scale = $scale , xTrans = $xTrans yTrans = $yTrans")

                            touchMatrix.setScale(scale, scale, centerX, centerY)
                            touchMatrix.postTranslate(xTrans, yTrans)

//                            rectMatrix.setTranslate(xTrans, yTrans)
//                            rectMatrix.preScale(scale, scale, centerX, centerY)

                            // 边界检查
                            if (transformLimit(touchMatrix, drawMatrix, drawRect, limitRectF)) {
                                invalidate()
                            }

                            lastPoint0.set(x0, y0)
                            lastPoint1.set(x1, y1)
                        }
                    } else {
                        if (state == State.MULTI_POINT) {
                            state = State.SINGLE_POINT
                            // 多指变单指时，从单指位置开始记录滑动坐标，等待下次move，计算具体偏移量
                            lastPoint0.set(x, y)
                        } else {
                            val xTrans = x - lastPoint0.x
                            val yTrans = y - lastPoint0.y
                            Log.i("Victor", "single point, xTrans = $xTrans yTrans = $yTrans")
                            touchMatrix.setTranslate(xTrans, yTrans)
                            lastPoint0.set(x, y)
                        }
                        // 边界检查
                        if (transformLimit(touchMatrix, drawMatrix, drawRect, limitRectF)) {
                            invalidate()
                        }
                    }
                }
                MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
                    parent.requestDisallowInterceptTouchEvent(false)
                }
            }
            return true
        }
        return super.onTouchEvent(event)
    }

    private val emptyMatrix: Matrix = Matrix()
    private val emptyRectF: RectF = RectF()
    private fun transformLimit(touchMatrix: Matrix,
                               drawMatrix: Matrix,
                               drawRectF: RectF,
                               limitRectF: RectF): Boolean {
        emptyMatrix.reset()
        emptyMatrix.set(touchMatrix)
        emptyRectF.setEmpty()
        emptyRectF.set(drawRectF)

        if (emptyMatrix.mapRect(emptyRectF)) {
            val outOfWidth = emptyRectF.width() > limitRectF.width()
            val outOfLeft = emptyRectF.left < 0
            val outOfRight = emptyRectF.right > measuredWidth

            val outOfTop = emptyRectF.top < 0
            val outOfBottom = emptyRectF.bottom > measuredHeight

            return if (outOfWidth || outOfLeft || outOfRight || outOfTop || outOfBottom) {
                // do nothing...
                false
            } else {
                drawMatrix.set(touchMatrix)
                true
            }
        }
        return false
    }

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        canvas?.apply {
            if (drawMatrix.mapRect(drawRect)) {
                drawPath.reset()
                drawPath.addCircle(drawRect.centerX(), drawRect.centerY(), drawRect.width() / 2f, Path.Direction.CW)
                drawPath(drawPath, drawPaint)
                drawRect(drawRect, debugPaint)
            }

            drawText("两指缩放，再拖拽", 100f, 100f, textPaint)
        }
    }


    enum class State {
        /**
         * 未触
         */
        NONE_POINT,

        /**
         * 单指
         */
        SINGLE_POINT,

        /**
         * 多指
         */
        MULTI_POINT
    }
}